/*
 * setblockcrc16.cc
 *
 *  Created on: 2011-1-6
 *      Author: xuhui
 */

#include <click/config.h>
#include "crc.h"
#include "setblockcrc16.hh"
#include <click/confparse.hh>
#include <click/error.hh>
#include <click/glue.hh>
# include <netinet/in.h>

CLICK_DECLS

SetBlockCRC16::SetBlockCRC16(){

}

SetBlockCRC16::~SetBlockCRC16(){

}

int
SetBlockCRC16::configure(Vector<String> &conf, ErrorHandler *errh){
	if (cp_va_kparse(conf, this, errh,
			"BLOCK_SIZE", cpkP+cpkM, cpUnsigned, &_block_size,
			cpEnd) < 0)
		return -1;
	return 0;
}

Packet *
SetBlockCRC16::simple_action(Packet *p){
	WritablePacket *p_out = 0;

	if (p->length() < sizeof(struct click_ether)) {
		click_chatter("%{element}: packet too small: %d vs %d\n",
				this,
				p->length(),
				sizeof(struct click_ether));
		p->kill();
		return 0;
	}

	click_ether *eh = (click_ether *) p->data();
	p->pull(sizeof(struct click_ether));
	uint32_t blocks=p->length()%_block_size ? p->length()/_block_size+1 : p->length()/_block_size;
	p_out=Packet::make(p->headroom(),0,2+blocks<<1+2+p->length(),p->tailroom());	// 2 for head crc size, blocks<<1 for block crc size, 2 for last block size
	if (!p_out) {
		return 0;
	}
	memset(p_out->data(),0,p_out->length());

	p_out->push(sizeof(struct click_ether));
	*(click_ether *)p_out->data()=*eh;

	unsigned char *ptr=p_out->data()+sizeof(struct click_ether)+2;

	for(uint32_t i=1;i<blocks;i++){
		memcpy(ptr,p->data(),_block_size);
		unsigned short crc = 0xffff;
		crc = update_crc16(crc, (char *) p->data(), _block_size);
		memcpy(ptr+_block_size,&crc,2);
		ptr+=_block_size+2;
		p->pull(_block_size);
	}

	memcpy(ptr,p->data(),p->length());
	ptr+=p->length();
	*(unsigned short *)ptr=htons(p->length());
	ptr+=2;
	unsigned short crc = 0xffff;
	crc = update_crc16(crc, (char *) ptr-p->length()-2, p->length()+2);
	memcpy(ptr,&crc,2);
	p->kill();

	return p_out;
}

enum {H_BLOCK_SIZE};

static String
SetBlockCRC16_read_param(Element *e, void *thunk)
{
	SetBlockCRC16 *td = (SetBlockCRC16 *)e;
    switch ((uintptr_t) thunk) {
      case H_BLOCK_SIZE:
	return String(td->_block_size) + "\n";
    default:
      return String();
    }
}

static int
SetBlockCRC16_write_param(const String &in_s, Element *e, void *vparam,
		      ErrorHandler *errh)
{
	SetBlockCRC16 *f = (SetBlockCRC16 *)e;
	String s = cp_uncomment(in_s);
	switch((intptr_t)vparam) {
		case H_BLOCK_SIZE: {
			int size;
			if (!cp_integer(s, &size))
				return errh->error("debug parameter must be boolean");
			f->_block_size=size;
			break;
		}
	}
	return 0;
}

void
SetBlockCRC16::add_handlers()
{
  add_read_handler("block_size", SetBlockCRC16_read_param, (void *) H_BLOCK_SIZE);

  add_write_handler("block_size", SetBlockCRC16_write_param, (void *) H_BLOCK_SIZE);
}

CLICK_ENDDECLS
EXPORT_ELEMENT(SetBlockCRC16)
